﻿using UnityEngine;
using System.Collections;

public class Character : Actor
{	
	public enum Direction
	{
		LEFT = -1,
		RIGHT = 1
	}

	public delegate void Callback();
	public Callback m_OnTargetReached;

	public Animator m_Animator;
	public float m_AnimSpeedModifier;
	public float m_HorizontalWalkingSpeed = 1f;
	public float m_VerticalWalkingSpeed = 1f;
	public float m_HurtDuration;
	[Tooltip("The collider that determines the size of the character. Should be set to Trigger.")]
	public Collider2D m_SizeCollider;
	public Collider2D m_AttackCollider;
	public Rigidbody2D m_Rigidbody2D;
	public SpriteRenderer m_SpriteRenderer;
	public SpriteRenderer m_ShadowRenderer;
	public Direction m_InitialDirection;
	
	public float m_MaxLife;
	protected float m_CurrentLife;
	public float CurrentLife
	{
		get { return m_CurrentLife; }
	}
	
	protected Direction m_Direction;
	protected Direction m_PreviousDirection;
	protected bool m_IsHurt;
	protected float m_HighestSpeed;
	protected Vector3 m_NewZIndex;
	protected Vector3 m_NewTarget;
	protected Vector3 m_TargetDirection;
	protected Color m_NewColor;
	protected ColliderSendMessage m_AttackColliderMessage;
	protected ValueReacher m_NewAlphaReacher;

	protected FightingMoveInfos m_MovesInfo;
	protected FightingMoveInfos.FightingMoveType m_CurrentFightingMoveType;

	private Vector2 m_NewVelocity;
	private Vector3 m_FlipVector;
	private Vector3 m_Size;
	public Vector3 Size
	{
		get { return m_Size; }
	}

	protected override void Awake()
	{
		base.Awake();

		m_IsHurt = false;
		m_Direction = m_InitialDirection;
		m_PreviousDirection = m_Direction;

		m_CurrentLife = m_MaxLife;
		
		if(GetComponent<FightingMoveInfos>() != null) m_MovesInfo = GetComponent<FightingMoveInfos>();

		if(m_SizeCollider != null) m_Size = m_SizeCollider.bounds.size;
		if(m_AttackCollider != null) m_AttackColliderMessage = m_AttackCollider.GetComponent<ColliderSendMessage>();
	}

	protected virtual void Update()
	{
	}

	protected virtual void OnHurtFinished()
	{
		m_IsHurt = false;
	}

	#region USEFUL FOR ALL CHARACTERS METHODS
	public void FlipSprite()
	{
		m_FlipVector = aTransform.localScale;
		m_FlipVector.y *= -1;
		aTransform.localScale = m_FlipVector;
		
		m_FlipVector = aTransform.eulerAngles;
		m_FlipVector.z += 180f;
		aTransform.eulerAngles = m_FlipVector;
	}
	
	/// <summary>
	/// Move the character according to a constant horizontal and vertical speed
	/// </summary>
	/// <param name="i_NormalizedDirection">The normalized direction.</param>
	/// <param name="i_Speed">The horizontal speed to set the velocity.</param>
	/// <param name="i_VerticalSpeed">The vertical speed to set the velocity.</param>
	public void Move(float i_HorizontalSpeed, float i_VerticalSpeed)
	{
		m_NewVelocity.x = i_HorizontalSpeed * Time.fixedDeltaTime;
		m_NewVelocity.y = i_VerticalSpeed * Time.fixedDeltaTime;
		m_Rigidbody2D.velocity = m_NewVelocity;
	}

	/// <summary>
	/// Move the character according to a direction and constant horizontal and vertical speed
	/// </summary>
	/// <param name="i_NormalizedDirection">The normalized direction.</param>
	/// <param name="i_Speed">The horizontal speed to set the velocity.</param>
	/// <param name="i_VerticalSpeed">The vertical speed to set the velocity.</param>
	public void Move(Vector3 i_NormalizedDirection, float i_HorizontalSpeed, float i_VerticalSpeed)
	{
		m_NewVelocity.x = i_NormalizedDirection.x * i_HorizontalSpeed * Time.fixedDeltaTime;
		m_NewVelocity.y = i_NormalizedDirection.y * i_VerticalSpeed * Time.fixedDeltaTime;

		m_Rigidbody2D.velocity = m_NewVelocity;
	}
	
	/// <summary>
	/// Move the character according to a direction and constant horizontal and vertical speed
	/// </summary>
	/// <param name="i_NormalizedDirection">The normalized direction.</param>
	/// <param name="i_Speed">The horizontal speed to set the velocity.</param>
	/// <param name="i_VerticalSpeed">The vertical speed to set the velocity.</param>
	/// <param name="i_MinVelY">Force a minimum velocity.</param>
	public void Move(Vector3 i_NormalizedDirection, float i_HorizontalSpeed, float i_VerticalSpeed, float i_MinVelY)
	{
		m_NewVelocity.x = i_NormalizedDirection.x * i_HorizontalSpeed * Time.fixedDeltaTime;
		m_NewVelocity.y = i_NormalizedDirection.y * i_VerticalSpeed * Time.fixedDeltaTime;

		if(m_NewVelocity.y < 0)
		{
			m_NewVelocity.y = Mathf.Clamp(m_NewVelocity.y, m_NewVelocity.y, -i_MinVelY);
		}
		else if(m_NewVelocity.y > 0)
		{
			m_NewVelocity.y = Mathf.Clamp(m_NewVelocity.y, i_MinVelY, m_NewVelocity.y);
		}
		
		m_Rigidbody2D.velocity = m_NewVelocity;
	}
	
	/// <summary>
	/// Moves to target.
	/// </summary>
	/// <param name="i_Target">Target.</param>
	/// <param name="i_Direction">Direction.</param>
	/// <param name="i_HorizontalSpeed">IHorizontal speed.</param>
	/// <param name="i_VerticalSpeed">Vertical speed.</param>
	/// <param name="i_Callback">A callback is called when the target has been reached.</param>
	public void MoveToTarget(Vector3 i_Target, Vector3 i_Direction, float i_HorizontalSpeed, float i_VerticalSpeed, Callback i_Callback)
	{		
		m_NewVelocity.x = i_Direction.x * i_HorizontalSpeed * Time.fixedDeltaTime;
		m_NewVelocity.y = i_Direction.y * i_VerticalSpeed * Time.fixedDeltaTime;
		m_Rigidbody2D.velocity = m_NewVelocity;
		
		m_NewVelocity.x = i_Target.x;
		m_NewVelocity.y = i_Target.y;

		if(Vector2.Distance(m_Rigidbody2D.position, m_NewVelocity) < 0.2f)
		{
			m_NewVelocity = Vector2.zero;
			if(i_Callback != null) i_Callback();
		}
	}

	public void SetTarget(Vector3 i_Target)
	{
		m_NewTarget = i_Target;
		m_TargetDirection = (i_Target - aTransform.position).normalized;
	}

	public void SetZIndex(float i_ZIndex)
	{		
		m_NewZIndex = aTransform.position;
		m_NewZIndex.z = i_ZIndex;
		aTransform.position = m_NewZIndex;
	}
	
	protected virtual void Hurt(float i_Damage)
	{
		m_IsHurt = true;

		m_CurrentLife -= i_Damage;

		if(m_CurrentLife <= 0)
		{
			OnDead();
		}
	}

	protected virtual void OnDead()
	{
		// Need to play an death anim and might just deactivate gameobject instead
		Destroy(aGameObject);
	}
	#endregion
}
